經過前面幾天的介紹,我們認識了許多常使用的 hooks,不過除了那些 hooks 之外,我們也可以將一些常用的共同程式邏輯抽取出來寫一個函式,這就是 Custom Hook。
在了解 Custom Hook 後,我們來實做一個 Custom Hook,這個 hook 是一個用來呼叫 Api 並進行一些處理的 Custom Hook,名字就叫 useAxios。
2022/1/26 更新 註: 這裡只是示範如果做一個 Custom hook,在呼叫 api 還有其他的做法,例如 redux dispatch async action + middleware,讀者可以自行依照狀況判斷使用~
在 codesandbox 新增一個檔案 useAxios,引入 axios 套件後,我們要使用 JSONplaceholder 的 api 作為 Base URL,等這個 hook 完成後會使用它來呼叫 JSONplaceholder 的 api。
在 useAxios 檔案我們宣告一個函式 useAxios,在做呼叫 api 時我們也會需要對一些像是呼叫 api 的載入時間和錯誤訊息做處理,所以建立兩個 state: isLoading 和 error,並且建立一個發出請求的函式 sendRequest,在這個函式內會對呼叫的 api 做處理,最後回傳一個包含 isLoading、error 和 sendRequest 的物件。
import { useState, useCallback } from "react";
import axios from "axios";
axios.defaults.baseURL = "https://jsonplaceholder.typicode.com";
const useAxios = () => {
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState(null);
  const sendRequest = useCallback(() => {}, []);
  return {
    isLoading,
    error,
    sendRequest,
  };
};
export default useAxios;
在 sendRequest 中,我們會傳入兩個參數 requestConfig 和 applyData,因為在呼叫每隻 api 的路徑、http 方法和其他參數都不太一樣,所以透過 requestConfig 可以將這些資訊帶入到 sendRequest 裡面,處理完後透過 callback function 也就是第二個參數 applyData 將呼叫 api 的結果回傳。
完成後的程式碼如下:
import { useState, useCallback } from 'react';
import axios from 'axios';
axios.defaults.baseURL = "https://jsonplaceholder.typicode.com";
const useAxios = () => {
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState(null);
  const sendRequest = useCallback(async (requestConfig, applyData) => {
    setIsLoading(true);
    setError(null);
    let res;
    try {
      res = await axios({
        url: requestConfig.url,
        method: requestConfig.method ? requestConfig.method : 'GET',
        headers: requestConfig.headers ? requestConfig.headers : {},
        data: requestConfig.data ? requestConfig.data : null,
      });
    } catch (error) {
      setError(error.message || 'Something went wrong!');
    } finally {
      if (res) {
        applyData(res.data);
      }
      setIsLoading(false);
    }
  }, []);
  return {
    isLoading,
    error,
    sendRequest,
  };
};
export default useAxios;
在 App 元件中呼叫幾支 JSONplaceholder 的 api。
import { useEffect, useState } from "react";
import useAxios from "./useAxios";
export default function App() {
  const [data, setData] = useState([]);
  const { isLoading, error, sendRequest: fetchData } = useAxios();
  const { sendRequest: createData } = useAxios();
  useEffect(() => {
    fetchData({ url: "/posts" }, (res) => {
      console.log(res);
      setData(res);
    });
  }, []);
  const clickNewData = () => {
    createData(
      {
        url: "/posts",
        method: "POST",
        data: {
          // id 沒加是因為 JSONPlaceholder 只做假新增不會更新到資料庫,所以 JSONPlaceholder 會自動產生一個假的 id
          userId: 10,
          title: "新增的物件",
          body: "example"
        }
      },
      (res) => console.log(res)
    );
  };
  if (isLoading) return <h1>Loading...</h1>;
  if (error) return <h1>{error}</h1>;
  return (
    <>
      <ul>
        {data.map((item) => (
          <li key={item.id}>
            <p>{item.title}</p>
          </li>
        ))}
      </ul>
      <button onClick={clickNewData}>新增</button>
    </>
  );
}
這次的實作程式碼在以下連結,另外附上 JSONplaceholder 的官網連結:
這是我在閱讀 React Docs 看到的,useCallback 的文件有提到推薦為 custom Hook 內的函式加上 useCallback。